package logic.ships.autoWeaponShip;

import java.util.List;

import com.jme.bounding.BoundingVolume;
import com.jme.math.Ray;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;

import logic.DelayedTPController;
import logic.common.team.Team;
import logic.nodes.TeamNode;
import logic.nodes.collision.CollidableNode;
import logic.nodes.lod.blocks.LeafBlock;
import logic.ships.frigate.Frigate;
import logic.ships.moveableShip.MovableShip;
import logic.weapons.Weapon;

public class AutoWeaponTPController extends DelayedTPController {
	
	private static final long serialVersionUID = 1L;
	private MovableShip ship;
	private Weapon weapon;
	private BlockFinder blockFinder;
	private AutoWeaponController controller;
	
	public AutoWeaponTPController(MovableShip ship, Weapon weapon, BlockFinder blockFinder, AutoWeaponController controller) {
		super(1f);
		assert(ship instanceof AutoWeaponShip);
		this.ship = ship;
		this.weapon = weapon;
		this.blockFinder = blockFinder;
		this.controller = controller;
		
		ship.getIngameState().getThreadPool().registerController(this);
	}
	
	@Override
	public void delayedUpdate(float time) {
		if(ship.isDestroyed()) ship.getIngameState().getThreadPool().unregisterController(this);
		
		TeamNode target = null;
		if(ship instanceof Frigate) target = ((Frigate)ship).getCurrentTarget();
		
		if(target == null || frigateUnderway(target)) target = setNewTarget();
		if(target != null) controller.setTarget(target, isVisible(target));
	}
	
	private boolean frigateUnderway(TeamNode target) {
		assert(target != null);
		if(ship instanceof Frigate) {
			float dist = ship.getLocalTranslation().distance(target.getLocalTranslation());
			return dist > ((Frigate)ship).getAutoWeaponShipProps().getMaxWeaponRange();
		}
		return false;
	}
	
	private boolean isVisible(TeamNode target) {
		Vector3f weaponLoc = weapon.getFireSpotLocation();
		Vector3f targetDir = target.getLocalTranslation().subtract(weaponLoc).normalize();
		Ray ray = new Ray(weaponLoc, targetDir);
		return isVisible(ship.getLowQualityNode(), ray);
	}
	
	private boolean isVisible(Node parent, Ray ray) {
		for(Spatial child : parent.getChildren()) {
			if(child instanceof Node) return isVisible((Node)child, ray);
			
			BoundingVolume bound = child.getWorldBound();
			if(child instanceof TriMesh 
			&& bound != null 
			&& !bound.intersects(weapon.getWorldBound()) 
			&& bound.getVolume() >= 400f 
			&& bound.intersects(ray)) return false;
		}
		return true;
	}
	
	protected MovableShip setNewTarget() {
		float minDist = ((AutoWeaponShip)ship).getFireRangeOf(weapon);
		MovableShip target = null;
		
		List<LeafBlock> blocks = blockFinder.getNearestLeafBlocks();
		for(int i = 0; i < blocks.size(); i++) {
			LeafBlock leafBlock = blocks.get(i);
			if(leafBlock == null) continue;
			
			List<CollidableNode> leafNodes = leafBlock.getNoneWeaponFireNodes();
			for(int j = 0; j < leafNodes.size(); j++) {
				CollidableNode node = leafNodes.get(j);
				if(node == null || !(node instanceof MovableShip)) continue;
				
				MovableShip s = (MovableShip)node;
				if(s.isDestroyed() || ship.getTeam() == s.getTeam()) continue;
				
				float dist = ship.getLocalTranslation().distance(s.getLocalTranslation());
				if(dist <= minDist) {
					target = s;
					minDist = dist;
				}
			}
		}
		
		Team enemyTeam = ship.getTeam().getEnemyTeam();
		for(Frigate frigate : enemyTeam.getFrigates()) {
			float dist = ship.getLocalTranslation().distance(frigate.getLocalTranslation());
			if(dist <= minDist) {
				target = frigate;
				minDist = dist;
			}
		}
		return target;
	}
}